'use strict';

const Service = require('./base');

const index = 'article';

class ArticleService extends Service {
  async selectById(id) {
    let result = null;
    try {
      const { hits: { hits } } = await this.app.elasticsearch.search({
        index,
        body: {
          query: {
            term: {
              id,
            },
          },
        },
      });
      result = hits[0]._source;
      result._id = hits[0]._id;
    } catch (err) {
      this.error(err);
    }

    return result;
  }

  async selectByKeyword(keyword) {
    let result = [];
    try {
      const { hits: { hits } } = await this.app.elasticsearch.search({
        index,
        body: {
          query: {
            multi_match: {
              query: keyword,
              fields: [ 'title', 'content' ],
              type: 'most_fields',
            },
          },
          from: 0,
          size: 10,
        },
      });
      result = hits;

    } catch (err) {
      this.error(err);
    }
    return result;
  }

  async selectList(from = 0, size = 20) {
    let result = [];
    try {
      const { hits: { hits } } = await this.app.elasticsearch.search({
        index,
        body: {
          sort: [
            { time: { order: 'desc' } },
            { sort: { order: 'asc' } },
          ],
          from,
          size,
        },
      });
      result = hits;
    } catch (err) {
      this.error(err);
    }

    return result;
  }

  async selectByCategory(firstCategoryId, secondCategoryId) {
    let result = [];
    try {
      const { hits: { hits } } = await this.app.elasticsearch.search({
        index,
        body: {
          query: {
            bool: {
              must: [
                { term: { first_category: firstCategoryId } },
                { term: { second_category: secondCategoryId } },
              ],
            },
          },
          sort: [{ sort: 'desc' }],
        },
      });
      result = hits;
    } catch (err) {
      this.error(err);
    }

    return result;
  }

  async selectNameByCategory(firstCategoryId) {
    let result = null;
    try {
      const { hits: { hits } } = await this.app.elasticsearch.search({
        index,
        body: {
          query: {
            bool: {
              must: [
                { term: { first_category: firstCategoryId } },
              ],
            },
          },
          sort: [{ sort: 'asc' }],
          from: 0,
          size: 100,
        },
      });
      result = hits;
    } catch (err) {
      this.error(err);
    }

    return result;
  }

  async insert(data) {
    let result = true;
    try {
      await this.app.elasticsearch.index({
        index,
        refresh: true,
        body: {
          ...data,
        },
      });
    } catch (err) {
      result = false;
      this.error(err);
    }

    return result;
  }

  /**
   * interfeace Props {
   *  id: string
   *  title: string
   *  sort: number
   *  first_category: string
   *  second_category: string
   * }
   * @param {Props[]} data 批量插入的数据
   *
   * interface Return {
   *  errors: Error[]
   *  count: number
   * }
   * @return {Return} 返回值
   */
  async bulkInsert(data) {
    const result = {
      errors: [],
      count: 0,
    };

    const body = data.flatMap(doc => [{ index: { _index: index } }, doc ]);
    const { errors, items } = await this.app.elasticsearch.bulk({ refresh: true, body });

    if (errors) {
      const erroredDocuments = [];
      items.forEach((action, i) => {
        const operation = Object.keys(action)[0];
        if (action[operation].error) {
          erroredDocuments.push({
            status: action[operation].status,
            error: action[operation].error,
            operation: body[i * 2],
            document: body[i * 2 + 1],
          });
        }
      });
      result.errors = erroredDocuments;
    }

    const { count } = await this.app.elasticsearch.count({ index });
    result.count = count;

    return result;
  }

  async update(elasticId, data) {
    let result = true;
    try {
      await this.app.elasticsearch.update({
        index,
        id: elasticId,
        refresh: true,
        body: {
          ...data,
        },
      });
    } catch (err) {
      result = false;
      this.error(err);
    }

    return result;
  }

  async deleteAll() {
    let result = true;
    try {
      await this.app.elasticsearch.deleteByQuery({
        index,
        refresh: true,
        body: {
          query: {
            match_all: {},
          },
        },
      });
    } catch (err) {
      result = false;
      this.error(err);
    }

    return result;
  }
}

module.exports = ArticleService;
